/*
 * Copyright (c) 2015 Cryptonomex, Inc., and contributors.
 *
 * The MIT License
 *
 * Permission is hereby granted, free of charge, to any person obtaining a copy
 * of this software and associated documentation files (the "Software"), to deal
 * in the Software without restriction, including without limitation the rights
 * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
 * copies of the Software, and to permit persons to whom the Software is
 * furnished to do so, subject to the following conditions:
 *
 * The above copyright notice and this permission notice shall be included in
 * all copies or substantial portions of the Software.
 *
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
 * THE SOFTWARE.
 */
#pragma once
#include <graphene/chain/protocol/base.hpp>
#include <graphene/chain/protocol/ext.hpp>

namespace graphene { namespace chain { 

   /**
    *  @class limit_order_create_operation
    *  @brief instructs the blockchain to attempt to sell one asset for another
    *  @ingroup operations
    *
    *  The blockchain will atempt to sell amount_to_sell.asset_id for as
    *  much min_to_receive.asset_id as possible.  The fee will be paid by
    *  the seller's account.  Market fees will apply as specified by the
    *  issuer of both the selling asset and the receiving asset as
    *  a percentage of the amount exchanged.
    *
    *  If either the selling asset or the receiving asset is white list
    *  restricted, the order will only be created if the seller is on
    *  the white list of the restricted asset type.
    *
    *  Market orders are matched in the order they are included
    *  in the block chain.
    */
   struct limit_order_create_operation : public base_operation
   {
       struct fee_parameters_type
       {
           uint64_t fee = GRAPHENE_BLOCKCHAIN_PRECISION / 10;
           uint32_t price_per_kbyte = 0 * GRAPHENE_BLOCKCHAIN_PRECISION;
           uint64_t min_real_fee = 0;
           uint16_t min_rf_percent = 0;
           extensions_type   extensions;
       };

      fee_type         fee;
      account_uid_type seller;
      asset            amount_to_sell;
      asset            min_to_receive;

      /// The order will be removed from the books if not filled by expiration
      /// Upon expiration, all unsold asset will be returned to seller
      time_point_sec expiration = time_point_sec::maximum();

      /// If this flag is set the entire order must be filled or the operation is rejected
      bool fill_or_kill = false;
      extensions_type   extensions;

      pair<asset_aid_type,asset_aid_type> get_market()const
      {
         return amount_to_sell.asset_id < min_to_receive.asset_id ?
                std::make_pair(amount_to_sell.asset_id, min_to_receive.asset_id) :
                std::make_pair(min_to_receive.asset_id, amount_to_sell.asset_id);
      }
      account_uid_type fee_payer_uid()const { return seller; }
      void             validate()const;
      share_type       calculate_fee(const fee_parameters_type& k)const;
      void get_required_active_uid_authorities(flat_set<account_uid_type>& a, bool enabled_hardfork)const
      {
          // Necessary balance of authority
          a.insert(seller);
      }
      price            get_price()const { return amount_to_sell / min_to_receive; }
   };


   /**
    *  @ingroup operations
    *  Used to cancel an existing limit order. Both fee_pay_account and the
    *  account to receive the proceeds must be the same as order->seller.
    *
    *  @return the amount actually refunded
    */
   struct limit_order_cancel_operation : public base_operation
   {
       struct fee_parameters_type
       {
           uint64_t fee = GRAPHENE_BLOCKCHAIN_PRECISION / 100;
           uint32_t price_per_kbyte = 0 * GRAPHENE_BLOCKCHAIN_PRECISION;
           uint64_t min_real_fee = 0;
           uint16_t min_rf_percent = 0;
           extensions_type   extensions;
       };

      fee_type             fee;
      limit_order_id_type  order;
      /** must be order->seller */
      account_uid_type     fee_paying_account;
      extensions_type      extensions;

      account_uid_type fee_payer_uid()const { return fee_paying_account; }
      void             validate()const;
      share_type       calculate_fee(const fee_parameters_type& k)const;
      void get_required_active_uid_authorities(flat_set<account_uid_type>& a, bool enabled_hardfork)const
      {
          // Necessary balance of authority
          a.insert(fee_paying_account);
      }
   };

   /**
    * @ingroup operations
    *
    * @note This is a virtual operation that is created while matching orders and
    * emitted for the purpose of accurately tracking account history, accelerating
    * a reindex.
    */
   struct fill_order_operation : public base_operation
   {
       struct fee_parameters_type
       {
           uint64_t fee = 0 * GRAPHENE_BLOCKCHAIN_PRECISION;
           uint32_t price_per_kbyte = 0 * GRAPHENE_BLOCKCHAIN_PRECISION;
           uint64_t min_real_fee = 0;
           uint16_t min_rf_percent = 0;
           extensions_type   extensions;
       };

      fill_order_operation(){}
      fill_order_operation( object_id_type o, account_uid_type a, asset p, asset r, asset f, price fp, bool m )
         :order_id(o),account_id(a),pays(p),receives(r),fee(f),fill_price(fp),is_maker(m) {}

      fee_type             fee; // paid by receiving account

      object_id_type       order_id;
      account_uid_type     account_id;
      asset                pays;
      asset                receives;
      price                fill_price;
      bool                 is_maker = false;

      pair<asset_aid_type,asset_aid_type> get_market()const
      {
         return pays.asset_id < receives.asset_id ?
                std::make_pair( pays.asset_id, receives.asset_id ) :
                std::make_pair( receives.asset_id, pays.asset_id );
      }
      account_uid_type fee_payer_uid()const { return account_id; }
      void             validate()const;
      share_type       calculate_fee(const fee_parameters_type& k) const;
      void get_required_active_uid_authorities(flat_set<account_uid_type>& a, bool enabled_hardfork)const
      {
          // Necessary balance of authority
          a.insert(account_id);
      }
   };

   struct market_fee_collect_operation : public base_operation
   {
      struct fee_parameters_type
      {
         uint64_t fee = 1 * GRAPHENE_BLOCKCHAIN_PRECISION;
         uint32_t price_per_kbyte = 0 * GRAPHENE_BLOCKCHAIN_PRECISION;
         uint64_t min_real_fee = 0;
         uint16_t min_rf_percent = 0;
         extensions_type   extensions;
      };

      fee_type          fee;
      account_uid_type  account;

      /// The asset to collect
      asset_aid_type    asset_aid;
      share_type        amount;

      extensions_type   extensions;

      account_uid_type  fee_payer_uid()const { return account; }
      void              validate()const;
      void get_required_active_uid_authorities(flat_set<account_uid_type>& a, bool enabled_hardfork)const
      {
         // need active authority
         a.insert(account);
      }
   };
} } // graphene::chain

FC_REFLECT(graphene::chain::limit_order_create_operation::fee_parameters_type, (fee)(price_per_kbyte)(min_real_fee)(min_rf_percent)(extensions))
FC_REFLECT(graphene::chain::limit_order_cancel_operation::fee_parameters_type, (fee)(price_per_kbyte)(min_real_fee)(min_rf_percent)(extensions))
FC_REFLECT(graphene::chain::fill_order_operation::fee_parameters_type, (fee)(price_per_kbyte)(min_real_fee)(min_rf_percent)(extensions)) // VIRTUAL
FC_REFLECT(graphene::chain::market_fee_collect_operation::fee_parameters_type, (fee)(price_per_kbyte)(min_real_fee)(min_rf_percent)(extensions))


FC_REFLECT( graphene::chain::limit_order_create_operation,(fee)(seller)(amount_to_sell)(min_to_receive)(expiration)(fill_or_kill)(extensions))
FC_REFLECT( graphene::chain::limit_order_cancel_operation,(fee)(fee_paying_account)(order)(extensions) )
FC_REFLECT( graphene::chain::fill_order_operation, (fee)(order_id)(account_id)(pays)(receives)(fill_price)(is_maker) )
FC_REFLECT( graphene::chain::market_fee_collect_operation, (fee)(account)(asset_aid)(amount)(extensions))
